// Package sms
// Created by GoLand
// @User: lenora
// @Date: 2023/12/29
// @Time: 09:15

package sms

import (
	"context"
	"errors"
	"sync/atomic"
)

// 服务商轮询发送
func (t *TopService) failoverSend(ctx context.Context, tplId string, args []string, phone ...string) error {
	for _, svc := range t.svcs {
		err := svc.Send(ctx, tplId, args, phone...)
		switch err {
		case nil:
			return nil
		case context.Canceled, context.DeadlineExceeded:
			//用户取消   超时
			return errors.New("")
		}
	}
	return errors.New("所有sms service都发送失败")
}

// 从上个成功的服务商开始轮询发送
func (t *TopService) failoverSendV1(ctx context.Context, tplId string, args []string, phone ...string) error {
	idx := t.idx
	length := uint64(len(t.svcs))
	for i := idx; i < idx+length; i++ {
		svc := t.svcs[i%length]
		err := svc.Send(ctx, tplId, args, phone...)
		switch err {
		case nil:
			t.idx = i
			return nil
		case context.Canceled, context.DeadlineExceeded:
			//用户取消   超时
			return errors.New("")
		}
	}
	return errors.New("所有sms service都发送失败")
}

// 从上个成功的服务商开始轮询发送(线程安全写法)
func (t *TopService) failoverSendV2(ctx context.Context, tplId string, args []string, phone ...string) error {
	idx := atomic.AddUint64(&t.idx, 1)
	length := uint64(len(t.svcs))
	for i := idx; i < idx+length; i++ {
		svc := t.svcs[i%length]
		err := svc.Send(ctx, tplId, args, phone...)
		switch err {
		case nil:
			return nil
		case context.Canceled, context.DeadlineExceeded:
			//用户取消   超时
			return err
		}
	}
	return errors.New("所有sms service都发送失败")
}

type TimeoutFailover interface {
	Check(svcs []BaseService) BaseService
	CountAdd()
	ResetCount()
}

type timeoutFailover struct {
	idx       int32
	count     int32 //连续超时次数
	threshold int32 //切换阈值

}

func NewTimeoutFailover(threshold int32) TimeoutFailover {
	return &timeoutFailover{
		threshold: threshold,
	}
}

func (t *timeoutFailover) Check(svcs []BaseService) BaseService {
	idx := atomic.LoadInt32(&t.idx)
	count := atomic.LoadInt32(&t.count)
	if count >= t.threshold {
		//超过阈值  服务切换
		//计算新的下标
		newIdx := (idx + 1) % int32(len(svcs))
		if atomic.CompareAndSwapInt32(&t.idx, idx, newIdx) {
			//重置计数器
			atomic.StoreInt32(&t.count, 0)
		}
	}
	return svcs[t.idx]
}

func (t *timeoutFailover) ResetCount() {
	atomic.StoreInt32(&t.count, 0)
	return
}

func (t *timeoutFailover) CountAdd() {
	atomic.AddInt32(&t.count, 1)
	return
}
